#include <benchmark/benchmark.h>
#include "stdio.h"
#include "stdarg.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "util.h"

using namespace std;
char buffer[100];

int MyPrintf1(FILE *stream, const char *format, ...)
{
    va_list args;
    int ret;
    va_start(args, format);
    ret = vfprintf(stdout, format, args);
    va_end(args);
    return (ret);
}

int MyPrintf2(const char *format, ...)
{
    va_list args;
    int ret;
    va_start(args, format);
    ret = vsprintf(buffer, format, args);
    va_end(args);
    return (ret);
}

int MyPrintf3(const char *format, ...)
{
    va_list args;
    int ret;
    va_start(args, format);
    ret = vsnprintf(buffer, 100, format, args);
    va_end(args);
    return (ret);
}

static void Bm_function_Fopen64(benchmark::State &state)
{
    for (auto _ : state)
    {
        FILE *fp = fopen("/data/data/fopen64.txt", "w+");
        if (fp == nullptr)
        {
            perror("fopen");
            exit(EXIT_FAILURE);
        }
        benchmark::DoNotOptimize(fp);
        if (fp != nullptr)
            fclose(fp);
    }

    state.SetBytesProcessed(state.iterations());
}

static void Bm_function_Fdopen(benchmark::State &state)
{
    int fp = open("test.txt", O_RDONLY | O_CREAT);
    FILE *fd = fdopen(fp, "r");
    if (fd == NULL)
    {
        perror("fdopen test");
        exit(EXIT_FAILURE);
    }

    for (auto _ : state)
    {
        benchmark::DoNotOptimize(fd);
    }

    if (fd != nullptr)
        fclose(fd);
    state.SetBytesProcessed(state.iterations());
}

static void Bm_function_Vfprintf(benchmark::State &state)
{
    const char *arr1 = "hello";
    const char *arr2 = "world";
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(MyPrintf1(stdout, "Set parameter %s %s success", arr1, arr2));
    }

    state.SetBytesProcessed(state.iterations());
} 

static void Bm_function_Vsprintf(benchmark::State &state)
{
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(MyPrintf2(buffer, "signal_stack", NULL));
    }

    state.SetBytesProcessed(state.iterations());
}

static void Bm_function_Vsnprintf(benchmark::State &state)
{
    const char *i = "holy";
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(MyPrintf3(buffer, 100, "Error loading shared library %s", i));
    }

    state.SetBytesProcessed(state.iterations());
}

static void Bm_function_Funlockfile(benchmark::State &state)
{
    FILE *fp = fopen("test.txt", "r");
    if (fp == NULL)
    {
        perror("fopen funlockfile");
        exit(EXIT_FAILURE);
    }

    for (auto _ : state)
    {
        funlockfile(fp);
    }

    state.SetBytesProcessed(state.iterations());
}

static void Bm_function_Rename(benchmark::State &state)
{
    char oldname[] = "file.txt";
    char newname[] = "newfile.txt";
    for (auto _ : state)
    {
        benchmark::DoNotOptimize(rename(oldname, newname));
    }
    state.SetBytesProcessed(state.iterations());
}

static void Bm_function_Fseek(benchmark::State &state)
{
    FILE* f = fopen("/data/data/test_fseek.txt", "w+");
    if (f == nullptr)
    {
        perror("fopen fseek");
        exit(EXIT_FAILURE);
    }
    for (auto _ : state)
    {
        char buf[] = "I love the world!";
        if(fwrite(buf, 1, sizeof(buf), f) < sizeof(buf))
        {
            int seek = fseek(f, 0, SEEK_SET);
            benchmark::DoNotOptimize(seek);
            if (seek > -1)
            {
                char second[] = "hello";
                fwrite(second, 1, sizeof(second), f);
            }
        }
    }
    fclose(f);
    state.SetItemsProcessed(state.iterations());
}

static void Bm_function_Sscanf(benchmark::State &state)
{
    for (auto _ : state)
    {
        int year, month, day;
        benchmark::DoNotOptimize(sscanf("20230515","%04d%02d%02d", &year, &month, &day));
    }
    state.SetBytesProcessed(state.iterations());
}

MUSL_BENCHMARK(Bm_function_Fopen64);
MUSL_BENCHMARK(Bm_function_Fdopen);
MUSL_BENCHMARK(Bm_function_Vfprintf);
MUSL_BENCHMARK(Bm_function_Vsprintf);
MUSL_BENCHMARK(Bm_function_Vsnprintf);
MUSL_BENCHMARK(Bm_function_Funlockfile);
MUSL_BENCHMARK(Bm_function_Rename);
MUSL_BENCHMARK(Bm_function_Fseek);
MUSL_BENCHMARK(Bm_function_Sscanf);